// Author: wsfuyibing <682805@qq.com>
// Date: 2025-04-11

package application

import (
	"context"
	"fmt"
	"gitee.com/go-libs/log"
	"gitee.com/go-wares/framework-gin/framework/config"
	"gitee.com/go-wares/framework-gin/framework/util/http/middlewares"
	"github.com/gin-gonic/gin"
	"sync"
)

var (
	Application Manager

	once = new(sync.Once)
)

type (
	Manager interface {
		AppConfig() *config.AppConfig
		GinConfig() *config.GinConfig
		GinEngine() *gin.Engine
		Start(ctx context.Context) (err error)
	}

	manager struct {
		appConfig *config.AppConfig
		ginConfig *config.GinConfig
		ginEngine *gin.Engine
	}
)

func (o *manager) AppConfig() *config.AppConfig          { return o.appConfig }
func (o *manager) GinConfig() *config.GinConfig          { return o.ginConfig }
func (o *manager) GinEngine() *gin.Engine                { return o.ginEngine }
func (o *manager) Start(ctx context.Context) (err error) { return o.start(ctx) }

// +---------------------------------------------------------------------------+
// | Access methods                                                            |
// +---------------------------------------------------------------------------+

func (o *manager) init() *manager {
	// Initialize application configurations.
	o.appConfig = (&config.AppConfig{}).Load()
	o.ginConfig = (&config.GinConfig{}).Load()

	// Initialize gin web framework.
	o.initGinEngine()
	return o
}

func (o *manager) initGinEngine() {
	// Config runtime mode.
	gin.SetMode(o.ginConfig.Mode)

	// Initialize gin engine.
	o.ginEngine = gin.New()
	o.ginEngine.Use(middlewares.Tracing, middlewares.Recover)

	// Config runtime variables.
	o.ginEngine.ContextWithFallback = o.ginConfig.ContextWithFallback
	o.ginEngine.ForwardedByClientIP = o.ginConfig.ForwardedByClientIP
	o.ginEngine.HandleMethodNotAllowed = o.ginConfig.MethodNotAllowed
	o.ginEngine.RedirectTrailingSlash = o.ginConfig.RedirectTrailingSlash
	o.ginEngine.RedirectFixedPath = o.ginConfig.RedirectFixedPath
	o.ginEngine.RemoveExtraSlash = o.ginConfig.RemoveExtraSlash
	o.ginEngine.UseH2C = o.ginConfig.UseH2C
	o.ginEngine.UseRawPath = o.ginConfig.UseRawPath
	o.ginEngine.UnescapePathValues = o.ginConfig.UnescapePathValues
}

func (o *manager) registerControllers() {}
func (o *manager) registerCrontabs()    {}

func (o *manager) start(ctx context.Context) (err error) {
	log.Infof(`server starting: host="%s", port="%d"`, o.appConfig.Host, o.appConfig.Port)

	// Wait log end before shutdown.
	defer func() {
		log.Infof(`server closed`)
		log.Close()
	}()

	// Register dependencies.
	o.registerControllers()
	o.registerCrontabs()

	err = o.ginEngine.Run(fmt.Sprintf(`%s:%d`, o.appConfig.Host, o.appConfig.Port))
	return err
}

func init() {
	once.Do(func() {
		Application = (&manager{}).init()
	})
}
